package com.furtherstar.auth.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.furtherstar.auth.controller.dto.RoleWorkQuery;
import com.furtherstar.auth.dao.enums.Disabled;
import com.furtherstar.auth.dao.mapper.AppRoleMapper;
import com.furtherstar.auth.dao.mapper.AuthUserMapper;
import com.furtherstar.auth.dao.mapper.CorpDeptMapper;
import com.furtherstar.auth.dao.mapper.CorpMapper;
import com.furtherstar.auth.dao.mapper.CorpPositionMapper;
import com.furtherstar.auth.dao.mapper.CorpWorkDeptMapper;
import com.furtherstar.auth.dao.mapper.CorpWorkMapper;
import com.furtherstar.auth.dao.mapper.UserRoleMapper;
import com.furtherstar.auth.dao.model.AppRole;
import com.furtherstar.auth.dao.model.AppRoleExample;
import com.furtherstar.auth.dao.model.AuthUser;
import com.furtherstar.auth.dao.model.Corp;
import com.furtherstar.auth.dao.model.CorpDept;
import com.furtherstar.auth.dao.model.CorpDeptExample;
import com.furtherstar.auth.dao.model.CorpExample;
import com.furtherstar.auth.dao.model.CorpPosition;
import com.furtherstar.auth.dao.model.CorpPositionExample;
import com.furtherstar.auth.dao.model.CorpWork;
import com.furtherstar.auth.dao.model.CorpWorkDept;
import com.furtherstar.auth.dao.model.CorpWorkDeptExample;
import com.furtherstar.auth.dao.model.CorpWorkExample;
import com.furtherstar.auth.dao.model.CorpWorkExample.Criteria;
import com.furtherstar.auth.dao.model.UserRole;
import com.furtherstar.auth.dao.model.UserRoleExample;
import com.furtherstar.auth.service.WorkService;
import com.furtherstar.core.context.Work;
import com.furtherstar.core.context.WorkDept;
import com.furtherstar.core.context.WorkWithRoles;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageSerializable;
import com.yummy.convention.exception.AssertUtil;
import com.yummy.convention.lang.IDGenerator;
import com.yummy.security.User;
import com.yummy.web.context.RequestContext;
import com.yummy.web.security.bean.DefaultRole;

@Service
public class WorkServiceImpl implements WorkService{
	@Autowired
	private CorpWorkMapper corpWorkMapper;
	@Autowired
	private CorpDeptMapper corpDeptMapper;
	@Autowired
	private CorpMapper corpMapper;
	@Autowired
	private CorpPositionMapper corpPositionMapper;
	@Autowired
	private UserRoleMapper userRoleMapper;
	@Autowired
	private CorpWorkDeptMapper corpWorkDeptMapper;
	@Autowired
	private AppRoleMapper appRoleMapper;
	@Autowired
	private AuthUserMapper authUserMapper;

	@Override
	public List<WorkWithRoles> listUserWorkWithRoles(Long userid) {
		List<CorpWork> corpWorks = getCorpWorks(userid);
		if(CollectionUtils.isEmpty(corpWorks)) {
			return Collections.emptyList();
		}
		List<Long> positionIds = corpWorks.stream().map(cw -> cw.getPositionId()).distinct().toList();
		Map<Long,CorpPosition> positionMap = getPositionMap(positionIds);
		List<Long> workIds = corpWorks.stream().map(cw -> cw.getId()).toList();
		List<CorpWorkDept> workDepts = getWorkDepts(workIds);
		Map<Long,CorpDept> deptMap = getCorpAndDeptMap(workDepts);
		Map<Long,List<CorpWorkDept>> workDeptMap = workDepts.stream().collect(Collectors.groupingBy(wd -> wd.getWorkId()));
		Map<Long,List<AppRole>> workRolesMap = getWorkRolesMap(workIds,RequestContext.getCurrentApp());
		return corpWorks.stream().map(cw -> {
			CorpPosition position = positionMap.get(cw.getPositionId());
			List<CorpWorkDept> currentWorkDepts = workDeptMap.get(cw.getId());
			List<AppRole> currentRoles = workRolesMap.get(cw.getId());
			CorpDept corp = deptMap.get(cw.getCorpId());
			if(position == null 
					|| corp == null) {
				return null;
			}
			WorkWithRoles work = toWorkWithRoles(cw,position,corp,currentWorkDepts,currentRoles,deptMap);
			return work;
		}).filter(w -> w!=null).toList();
	}

	private List<CorpWork> getCorpWorks(Long userid) {
		CorpWorkExample example = new CorpWorkExample();
		example.createCriteria().andUseridEqualTo(userid);
		return corpWorkMapper.selectByExample(example);
	}
	
	private WorkWithRoles toWorkWithRoles(CorpWork cw, CorpPosition position, CorpDept corp, List<CorpWorkDept> corpWorkDepts, List<AppRole> appRoles, Map<Long, CorpDept> deptMap) {
		WorkWithRoles w = new WorkWithRoles();
		w.setCorpId(corp.getId());
		w.setCorpName(corp.getName());
		w.setDepts(corpWorkDepts == null ? Collections.emptyList() : corpWorkDepts.stream().map(cwd -> {
			CorpDept dept = deptMap.get(cwd.getDeptId());
			if(dept == null) {
				return null;
			}
			WorkDept wd = new WorkDept();
			wd.setId(cwd.getDeptId());
			wd.setName(dept.getName());
			wd.setManager(cwd.getManager());
			return wd;
		}).filter(cwd -> cwd != null).toList());
		w.setId(cw.getId());
		w.setPositionId(position.getId());
		w.setPositionLevel(cw.getPositionLevel());
		w.setPositionName(position.getName());
		w.setPositionType(position.getType());
		w.setPrimary(cw.getPrimary() == null ? false : cw.getPrimary() == 1);
		w.setRoles(appRoles == null ? Collections.emptyList() : appRoles.stream().map(ar -> {
			DefaultRole role = new DefaultRole();
			role.setId(ar.getId());
			role.setName(ar.getName());
			role.setAppname(ar.getAppname());
			return role;
		}).toList());
		return w;
	}

	private Map<Long, List<AppRole>> getWorkRolesMap(List<Long> workIds, String appname) {
		List<UserRole> userRoles = getUserRoles(appname, workIds);
		if(CollectionUtils.isEmpty(userRoles)) {
			return Collections.emptyMap();
		}
		Map<Long,AppRole> roleMap = getRoleMap(userRoles.stream().map(ur -> ur.getRoleId()).distinct().toList());
		Map<Long,List<UserRole>> workUserRolesMap = userRoles.stream().collect(Collectors.groupingBy(ur -> ur.getWorkId()));
		Map<Long,List<AppRole>> workRolesMap = new HashMap<>();
		for(Entry<Long,List<UserRole>> entry : workUserRolesMap.entrySet()) {
			workRolesMap.put(entry.getKey(), entry.getValue().stream().map(ur -> {
				return roleMap.get(ur.getRoleId());
			}).toList());
		}
		return workRolesMap;
	}

	private List<UserRole> getUserRoles(String appname, List<Long> workIds) {
		UserRoleExample example = new UserRoleExample();
		example.createCriteria().andAppnameEqualTo(appname).andWorkIdIn(workIds);
		return userRoleMapper.selectByExample(example);
	}

	private Map<Long, CorpDept> getCorpAndDeptMap(List<CorpWorkDept> workDepts) {
		List<Long> allDeptIds = workDepts.stream().flatMap(wd -> {
			List<Long> deptIds = new ArrayList<>();
			deptIds.add(wd.getDeptId());
			deptIds.add(wd.getCorpId());
			return deptIds.stream();
		}).distinct().toList();
		return getCorpDeptMap(allDeptIds);
	}

	private Map<Long, CorpDept> getCorpDeptMap(List<Long> allDeptIds) {
		CorpDeptExample example = new CorpDeptExample();
		example.createCriteria().andIdIn(allDeptIds).andDisabledEqualTo(Disabled.normal.ordinal());
		List<CorpDept> depts = corpDeptMapper.selectByExample(example);
		if(CollectionUtils.isEmpty(depts)) {
			return Collections.emptyMap();
		}
		return depts.stream().collect(Collectors.toMap(d -> d.getId(), d -> d));
	}

	private List<CorpWorkDept> getWorkDepts(List<Long> workIds) {
		CorpWorkDeptExample example = new CorpWorkDeptExample();
		example.createCriteria().andWorkIdIn(workIds);
		return corpWorkDeptMapper.selectByExample(example);
	}

	private Map<Long, AppRole> getRoleMap(List<Long> roleIds) {
		List<AppRole> appRoles = getAppRoles(roleIds);
		if(CollectionUtils.isEmpty(appRoles)) {
			return Collections.emptyMap();
		}
		return appRoles.stream().collect(Collectors.toMap(ar -> ar.getId(), ar -> ar));
	}

	private List<AppRole> getAppRoles(List<Long> roleIds) {
		AppRoleExample example = new AppRoleExample();
		example.createCriteria()
			.andIdIn(roleIds);
		return appRoleMapper.selectByExample(example);
	}

	@Override
	public PageSerializable<Work> queryRoleWorks(RoleWorkQuery roleWorkQuery) {
		List<UserRole> userRoles = queryUserRolesByRole(roleWorkQuery);
		if(CollectionUtils.isEmpty(userRoles)) {
			return PageSerializable.of(Collections.emptyList());
		}
		List<Long> workIds = userRoles.stream().map(ur -> ur.getWorkId()).toList();
		List<CorpWork> corpWorks = getCorpWorks(workIds);
		Map<Long,CorpPosition> positionMap = getPositionMap(corpWorks.stream().map(w -> w.getPositionId()).distinct().toList());
		Map<Long,Corp> corpMap = getCorpMap(corpWorks.stream().map(w -> w.getCorpId()).distinct().toList());
		List<Work> works = corpWorks.stream().filter(cw -> {
			return positionMap.containsKey(cw.getPositionId())
					&& corpMap.containsKey(cw.getCorpId());
		}).map(cw -> {
			return toWork(cw,positionMap.get(cw.getPositionId()),corpMap.get(cw.getCorpId()));
		}).toList();
		PageSerializable<UserRole> userRolePage = PageSerializable.of(userRoles);
		PageSerializable<Work> page = PageSerializable.of(works);
		page.setTotal(userRolePage.getTotal());
		return page;
	}

	private Map<Long, Corp> getCorpMap(List<Long> corpIds) {
		CorpExample example = new CorpExample();
		example.createCriteria().andIdIn(corpIds)
			.andDisabledEqualTo(Disabled.normal.ordinal());
		List<Corp> list = corpMapper.selectByExample(example);
		if(CollectionUtils.isEmpty(list)) {
			return Collections.emptyMap();
		}
		return list.stream().collect(Collectors.toMap(d -> d.getId(), d -> d));
	}

	private Work toWork(CorpWork cw, CorpPosition position, Corp corp) {
		Work w = new Work();
		w.setCorpId(cw.getCorpId());
		w.setCorpName(corp.getName());
		w.setId(cw.getId());
		w.setPositionId(cw.getPositionId());
		w.setPositionLevel(cw.getPositionLevel());
		w.setPositionName(position.getName());
		w.setPositionType(position.getType());
		w.setPrimary(cw.getPrimary() == 1 ? true : false);
		return w;
	}

	private Map<Long, CorpPosition> getPositionMap(List<Long> positionIds) {
		CorpPositionExample example = new CorpPositionExample();
		example.createCriteria().andIdIn(positionIds);
		List<CorpPosition> positions = corpPositionMapper.selectByExample(example);
		if(CollectionUtils.isEmpty(positions)) {
			return Collections.emptyMap();
		}
		return positions.stream().collect(Collectors.toMap(p -> p.getId(), p -> p));
	}

	private List<CorpWork> getCorpWorks(List<Long> workIds) {
		CorpWorkExample example = new CorpWorkExample();
		example.createCriteria().andIdIn(workIds);
		return corpWorkMapper.selectByExample(example);
	}

	private List<UserRole> queryUserRolesByRole(RoleWorkQuery roleWorkQuery) {
		PageHelper.startPage(roleWorkQuery.getPageNum(),roleWorkQuery.getPageSize());
		UserRoleExample example = new UserRoleExample();
		UserRoleExample.Criteria criteria = example.createCriteria().andRoleIdEqualTo(roleWorkQuery.getRoleId());
		if(StringUtils.isNotBlank(roleWorkQuery.getUserKeyword())) {
			criteria.andNicknameLike("%"+roleWorkQuery.getUserKeyword()+"%");
		}
		return userRoleMapper.selectByExample(example);
	}

	@Override
	@Transactional
	public Long add(Work work) {
		AssertUtil.isTrue(!exist(work),"工作不可重复");
		AuthUser user = getAuthUser(work.getUserid());
		AssertUtil.notNull(user);
		CorpWork corpWork = extractCorpWork(work,user);
		List<CorpWorkDept> workDepts = extractWorkDepts(work,corpWork,user);
		corpWorkMapper.insert(corpWork);
		if(!CollectionUtils.isEmpty(workDepts)) {
			corpWorkDeptMapper.insertBatch(workDepts);
		}
		if(corpWork.getPrimary() == 1) {
			setOtherNoPrimary(user.getId(),corpWork.getId());
		}
		return corpWork.getId();
	}

	private void setOtherNoPrimary(Long userid, Long workId) {
		CorpWorkExample example = new CorpWorkExample();
		example.createCriteria().andUseridEqualTo(userid).andIdNotEqualTo(workId);
		CorpWork cw = new CorpWork();
		cw.setPrimary(0);
		corpWorkMapper.updateByExampleSelective(cw, example);
	}

	private List<CorpWorkDept> extractWorkDepts(Work work, CorpWork corpWork, AuthUser user) {
		if(CollectionUtils.isEmpty(work.getDepts())) {
			return null;
		}
		return work.getDepts().stream().map(d -> {
			CorpWorkDept wd = new CorpWorkDept();
			wd.setId(IDGenerator.getId());
			wd.setCorpId(corpWork.getCorpId());
			wd.setDeptId(d.getId());
			wd.setManager(d.getManager() == null ? 0 : d.getManager());
			wd.setPositionId(corpWork.getPositionId());
			wd.setUserid(user.getId());
			wd.setUsername(user.getUsername());
			wd.setWorkId(corpWork.getId());
			wd.setNickname(user.getNickname());
			return wd;
		}).toList();
	}

	private AuthUser getAuthUser(Long userid) {
		return authUserMapper.selectByPrimaryKey(userid);
	}

	private CorpWork extractCorpWork(Work work, AuthUser user) {
		CorpWork cw = new CorpWork();
		cw.setAvatar(user.getAvatar());
		cw.setCorpId(work.getCorpId());
		cw.setNickname(user.getNickname());
		cw.setPositionId(work.getPositionId());
		cw.setPositionLevel(work.getPositionLevel());
		cw.setPrimary(work.getPrimary() == null ? 0 : work.getPrimary() ? 1 : 0);
		cw.setUsename(user.getUsername());
		cw.setUserid(user.getId());
		cw.setId(work.getId() == null ? IDGenerator.getId() : work.getId());
		User currentUser = RequestContext.getCurrentUser();
		cw.setLmNickname(currentUser.getNickname());
		cw.setLmUserid((Long) currentUser.getUserid());
		cw.setLmTime(new Date());
		return cw;
	}

	private boolean exist(Work work) {
		CorpWorkExample example = new CorpWorkExample();
		Criteria criteria = example.createCriteria().andPositionIdEqualTo(work.getPositionId())
			.andUseridEqualTo(work.getUserid());
		if(work.getId() != null) {
			criteria.andIdNotEqualTo(work.getId());
		}
		return corpWorkMapper.countByExample(example) != 0;
	}

	@Override
	@Transactional
	public Long update(Work work) {
		AssertUtil.isTrue(!exist(work),"工作不可重复");
		AuthUser user = getAuthUser(work.getUserid());
		AssertUtil.notNull(user);
		CorpWork corpWork = extractCorpWork(work,user);
		List<CorpWorkDept> workDepts = extractWorkDepts(work,corpWork,user);
		corpWorkMapper.updateByPrimaryKey(corpWork);
		deleteWorkDeptsByWork(corpWork.getId());
		if(!CollectionUtils.isEmpty(workDepts)) {
			corpWorkDeptMapper.insertBatch(workDepts);
		}
		if(corpWork.getPrimary() == 1) {
			setOtherNoPrimary(user.getId(),corpWork.getId());
		}
		return corpWork.getId();
	}

	private void deleteWorkDeptsByWork(Long workId) {
		CorpWorkDeptExample example = new CorpWorkDeptExample();
		example.createCriteria().andWorkIdEqualTo(workId);
		corpWorkDeptMapper.deleteByExample(example);
	}

	@Override
	@Transactional
	public Boolean delete(Long workId) {
		int row = corpWorkMapper.deleteByPrimaryKey(workId);
		AssertUtil.isTrue(row == 1,"找不到记录");
		deleteRelated(workId);
		return true;
	}

	private void deleteRelated(Long workId) {
		deleteWorkDeptsByWork(workId);
		deleteUserRolesByWork(workId);
	}

	private void deleteUserRolesByWork(Long workId) {
		UserRoleExample example = new UserRoleExample();
		example.createCriteria().andWorkIdEqualTo(workId);
		userRoleMapper.deleteByExample(example);
	}

}
